Skip to content

feat: add --server-port option to connect to external vLLM server - #168

Merged
SrGonao merged 62 commits into
EleutherAI:mainfrom
CharlesR-W:feature/vllm-server-mode
Nov 2, 2026
Merged

SrGonao merged 62 commits into
EleutherAI:mainfrom
CharlesR-W:feature/vllm-server-mode

Conversation

@CharlesR-W

@CharlesR-W CharlesR-W commented Jan 15, 2026 •

Copy link
Copy Markdown
Contributor

Connect to an external OpenAI-compatible chat server with --server_port 8000 or --server_url http://host:8000/v1/, while retaining in-process vLLM as the default. Optional server authentication uses SERVER_API_KEY.

The revised implementation uses a separate HTTP ServerClient. It does not import or initialize vLLM on the server path, forwards generation options such as seed/top_p/stop, parses generated-token log probabilities, propagates HTTP failures, and closes HTTP resources even when the pipeline fails. In-process Offline retains its existing interface.

Client capabilities select simulation mode: external chat servers use JSON simulation, because they do not provide prompt log probabilities. Unsupported prompt-logprob and streaming requests fail explicitly instead of returning silent all-zero scores. The local Offline client retains prompt-logprob simulation.

Validation: 11 focused server/classifier/tokenizer cases pass with runtime checking, including a nonzero end-to-end JSON simulation through an HTTP mock, authentication/request forwarding, probability parsing, error propagation, resource cleanup, and a subprocess proving server import does not import vLLM. python -m delphi --help and repository Ruff pass. A live vLLM GPU server was not used. The full Actions run has been approved and is running.

When set, uses OpenAI-compatible API to talk to a vLLM server instead
of loading the model in-process. Default behavior unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Jan 15, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
5 out of 6 committers have signed the CLA.

✅ CharlesR-W
✅ SrGonao
✅ WatchTree-19
✅ robbiebusinessacc
✅ flyingdoubleG
❌ semantic-release


semantic-release seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

pre-commit-ci Bot and others added 28 commits January 15, 2026 23:58
updates:
- [github.com/psf/black-pre-commit-mirror: 25.11.0 → 26.1.0](psf/black-pre-commit-mirror@25.11.0...26.1.0)
- [github.com/astral-sh/ruff-pre-commit: v0.14.4 → v0.14.13](astral-sh/ruff-pre-commit@v0.14.4...v0.14.13)
…e-config

[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black-pre-commit-mirror: 26.1.0 → 26.3.0](psf/black-pre-commit-mirror@26.1.0...26.3.0)
- [github.com/astral-sh/ruff-pre-commit: v0.14.13 → v0.15.5](astral-sh/ruff-pre-commit@v0.14.13...v0.15.5)
…e-config

[pre-commit.ci] pre-commit autoupdate
Automatically generated by python-semantic-release
…eanup

Move to cpu when computing firing counts
fix(ci): add uv quality gates and test discovery config
…ntract

feat(sparse-coders): allow for sparse-coder random initialization
Automatically generated by python-semantic-release
On transformers >= 5, batch_decode treats a 1-D tensor as a single
sequence and returns one joined string instead of per-token strings.
This silently broke token highlighting in the explainer and classifier
scorers and raised IndexError in the intruder scorer.

Add decode_per_token to delphi.utils and use it at the four sites that
need per-token strings (samplers, non-activating constructor, OpenAI
simulator). Fixes EleutherAI#176
Signed-off-by: WatchTree-19 <119982314+WatchTree-19@users.noreply.github.com>
…injection

parse_explanation used re.search(r"\[EXPLANATION\]:\s*(.*)", text, re.DOTALL)
— first-match with greedy-to-EOT capture. Two failure modes:

1. CoT echo (parsing defect): when the explainer reasoned in chain-of-thought
   and wrote [EXPLANATION]: while thinking (considering then rejecting a
   hypothesis), greedy .* with re.DOTALL swallowed the entire response from
   the first marker to end-of-text. The label became the whole reasoning
   chain rather than the final verdict.

2. Marker injection (security): first-match binding let an early
   [EXPLANATION]: token win over the explainer's actual verdict. The
   highlighted examples in the explainer's prompt (top-activating text from
   the subject model) are injected verbatim, so a subject model whose
   top-activating text contains '[EXPLANATION]: <benign>' can cause the
   explainer to echo it, and the echoed marker wins. The resulting label
   flows verbatim into DetectionScorer / IntruderScorer and operator-facing
   tooling (Neuronpedia auto-interp).

Fix: last-match binding with non-greedy capture (no re.DOTALL). Each
[EXPLANATION]: marker binds to only its own line; taking the last match
selects the explainer's final verdict. Defeats both CoT echo and early
marker injection; preserves clean single-marker parsing.

Residual: if an injected marker is the literal last line, last-match still
binds to it. The complete defense (response-aware scrub of any marker
appearing in the highlighted-examples input) is a follow-up; this PR closes
the common cases.

Adds 7 regression tests covering clean parsing, CoT echo, last-match
ordering, marker injection, and edge cases.
…token-decode

fix: decode str_tokens per token for transformers >= 5
…metric

fix: failed_count is always zero in result analysis
…explanation-injection

fix(explainer): harden parse_explanation against CoT echo and marker injection
…zers

The logprob simulator indexed the result of apply_chat_template as a list
of token ids and took encode('\t')[1] as the tab token, assuming a BOS
token. On transformers >= 5, apply_chat_template returns a BatchEncoding
by default, and tokenizers such as Qwen2.5 add no BOS, so both raised
IndexError. simulate() catches the exception and returns all-zero
predictions, so every simulation correlation became NaN without an error.

Request a list with return_dict=False and encode the tab without special
tokens. The offline client also passed apply_chat_template's result to
TokensPrompt as token ids; request a list there too.
- load_data globbed '*{module}*', so with hookpoints 'layers.1' and
  'layers.10' the score files of layers.10 were also loaded, and
  counted, as layers.1. Match '{module}_latent*', the score file name.
- frequency_weighted_f1 indexed the firing-count tensor with a pandas
  Series. Torch reads it positionally (series[0]) while pandas looks up
  labels, so every module after the first raised KeyError: 0 and
  log_results crashed for runs with several hookpoints.
- The firing count was set only when 'latent_idx in counts[module]', a
  test on the tensor's values; check the index range instead.
sampler() draws the explainer's train examples and the scorers' test
examples from the same record.examples. With the default config both use
split_quantiles, which reseeds the global RNG with the same seed, so in
quantile 0 random.sample(q, 4) and random.sample(q, 5) make the same first
4 picks: 4 of the 5 top-quantile test examples are always train examples.
In total 10-20 of the 50 default test examples had been shown to the
explainer, which inflates detection and fuzzing scores.

Exclude the train examples when sampling the test examples, within the
original quantile bins.
…r quantile

The internal intruder type removed the source quantile from the list of
quantiles with list.remove, which compares the quantile lists for
equality. Lists of the same length are compared element by element, and
ActivatingExample's dataclass __eq__ compares its tensors, which raises
'Boolean value of Tensor with more than one value is ambiguous'. With the
default sampler every quantile holds the same number of examples, so
every batch whose quantile is not the first one crashed.

Select the other quantiles by their key instead.
SrGonao and others added 28 commits October 31, 2026 18:08
…sis-modules

fix: per-module aggregation in result analysis with several hookpoints
…at-template-ids

fix: simulator returns zeros on transformers >= 5 and BOS-less tokenizers
…e-config

[pre-commit.ci] pre-commit autoupdate
…-isolation

fix: isolate explainer tests and unblock pytest collection
…-mark-sets

fix: match fuzzing mark distributions between labels
…ry-parsing

fix: validate classifier answers and score partial batches
…dow-sampling

fix: correct centering and sample distinct negative windows
…-display

fix: preserve latent records when saving and displaying examples
docs: clarify release history and deduplicate the changelog
…ic-activations

fix: accept integer simulator activations under runtime checking
@SrGonao
SrGonao merged commit 00a5645 into EleutherAI:main Nov 2, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants